﻿namespace EFS.Common.Async
{
	/// Class Repeater
	public sealed class Repeater : System.IDisposable
	{
		#region Composition

		// sync access
		private System.Object syncAccess = new System.Object();

		// wait start
		private System.Threading.ManualResetEvent waitStartEvent = null;

		// wait stop
		private System.Threading.ManualResetEvent waitStopEvent = null;

		// repeat
		private System.Threading.ManualResetEvent waitRepeateEvent = null;

		// stop flag
		private System.Int32 stop = 0;

		// repeater thread
		private System.Threading.Thread repeaterThread = null;

		#endregion

		#region Delegates

		/// <summary>
		/// Deployment changes watch
		/// </summary>
		public delegate void Repeate();

		/// <summary>
		/// Repeate delegate registrar
		/// </summary>
		public Repeate OnRepeate;

		#endregion

		#region Constructors
		
		/// <summary>
		/// Static constructor to init local instance
		/// </summary>
		static Repeater()
		{
			Instance = new Repeater();
		}

		/// <summary>
		/// Contructor
		/// </summary>
		private Repeater()
		{
		}

		#endregion

		#region Usage

		/// Property Instance (singleton): Get
		public static Repeater Instance { get; private set; }

		/// <summary>
		/// Check if repeater is active
		/// </summary>
		public System.Boolean IsRepeaterActive
		{
			get
			{
				// lock and get state
				// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
				lock (syncAccess)
				{
					return repeaterThread.ThreadState == System.Threading.ThreadState.Running;
				}
			}
		}

		/// <summary>
		/// Start repeater, wait to start
		/// </summary>
		public void StartRepeater()
		{
			// only one can start repeater
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// must not have thread
				if (null != repeaterThread)
				{
					throw new System.InvalidOperationException("Repeater thread already started");
				}

				// reset stop
				stop = 0;

				// sanity check
				EFS.Common.Verify.Condition(null == waitStartEvent, "Repeater Unexpected state waitStartEvent != null");

				// make event
				waitStartEvent = new System.Threading.ManualResetEvent(false);

				// must be null
				EFS.Common.Verify.Condition(null == waitRepeateEvent, "Repeater Wrong waitRepeateEvent state: should be null");

				// create new wait event
				waitRepeateEvent = new System.Threading.ManualResetEvent(false);

				// create new thread
				repeaterThread = new System.Threading.Thread(new System.Threading.ThreadStart(LogThreadProc));
				repeaterThread.Start();

				// wait start
				waitStartEvent.WaitOne();

				// release
				waitStartEvent.Close();

				// clean
				waitStartEvent = null;
			}
		}

		/// Stop
		public void StopRepeater()
		{
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// must have thread
				if (null == repeaterThread)
				{
					throw new System.InvalidOperationException("Repeater thread not started");
				}

				// check state
				if (repeaterThread.ThreadState == System.Threading.ThreadState.Running ||
					repeaterThread.ThreadState == System.Threading.ThreadState.WaitSleepJoin)
				{
					// sanity check
					EFS.Common.Verify.Condition(null == waitStopEvent, "Repeater Unexpected state waitStopEvent != null");

					// make event
					waitStopEvent = new System.Threading.ManualResetEvent(false);

					// set signal to thread
					stop = 1;

					// create new wait event
					waitRepeateEvent.Set();

					// wait stop
					if (waitStopEvent.WaitOne(500))
					{
						// abort
						repeaterThread.Abort();
					}

					// release
					waitStopEvent.Close();

					// clean
					waitStopEvent = null;
				}

				// let GC take care of it
				repeaterThread = null;
			}
		}

		#endregion

		#region Runtime

		/// Working thread procedure
		private void LogThreadProc()
		{
			// check if some one is wating
			if (null != waitStartEvent)
			{
				// wait set
				waitStartEvent.Set();
			}

			// cicle
			while (stop != 1)
			{
				try
				{
					// call all delegates
					if (null != OnRepeate)
					{
						OnRepeate();
					}

					// wait repeate 1sc
					waitRepeateEvent.WaitOne(1000);
				}
				catch (System.Threading.ThreadAbortException)
				{
					// bye
					break;
				}
				catch (System.Exception ex)
				{
					EFS.EventLog.Log.LogException(ex, null);
				}
			}

			// check if some one is wating
			if (null != waitStopEvent)
			{
				// wait set
				waitStopEvent.Set();
			}
		}

		#endregion

		#region Disposable

		/// <summary>
		/// Dispose
		/// </summary>
		public void Dispose()
		{
			try
			{
				if (waitStartEvent != null)
				{
					waitStartEvent.Close();
					waitStartEvent = null;
				}

				if (waitStopEvent != null)
				{
					waitStopEvent.Close();
					waitStopEvent = null;
				}

				if (waitRepeateEvent != null)
				{
					waitRepeateEvent.Close();
					waitRepeateEvent = null;
				}
			}
			catch (System.Exception ex)
			{
				EFS.EventLog.Log.LogException(ex, null);
			}
		}

		/// <summary>
		/// Destructor
		/// </summary>
		~Repeater()
		{
			Dispose();
		}

		#endregion
	}

}